package com.contexttree.filereader;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

import javax.swing.JTextArea;

/**
 * This class manage all operations on TXT file
 * Politecnico di Milano
 * @author Paolo Barbaglia
 * @author Filippo Ferrari
 *
 */
public class ReadTxtFile {
	
	/**
	 * This method read line by line a txt file and return an array list of string
	 * @param filename
	 * @return
	 */
	public static  ArrayList<String> read(String filename) {
        ArrayList<String> stringList = new ArrayList<String>();
        
		try{
			FileInputStream fstream = new FileInputStream(filename);
	        DataInputStream in = new DataInputStream(fstream);
	        BufferedReader br = new BufferedReader(new InputStreamReader(in));
	        String strLine;	       
	        
	        while ((strLine = br.readLine()) != null)
	        	
	        	stringList.add(strLine);

	        in.close();
	        
	    }catch (Exception e){
	        System.err.println("Errore: " + e.getMessage());
	    }
		return stringList;	
	}
	
	/**
	 * This method order a array list of string elements and return an array list ordered
	 * @param txtFile Array list of string
	 * @return txtFile Array list of string ordered
	 */
	public static ArrayList<String> order(ArrayList<String> txtFile){
		
		Collections.sort(txtFile);
	    
		return txtFile;
		
	}
	
	/**
	 * This method calculate number of views covered by a record and percentage. It receive the ArrayList<String> txtFileOrdered and String [][] table
	 * (result of ReadCsvFile.checkRecords) and return the String[][] tableOrdered, that is equals to table, but it contains two other fields:
	 * "Views.Covered" (the views are contained in txtFileOrdered) and "Covered.Percent".The String[][] tableOrdered is ordered by the value "Views.Covered".
	 * @param table
	 * @param txtFileOrdered
	 * @return tableOrdered
	 */
	public static String[][] orderRecords (String [][] table, ArrayList<String> txtFileOrdered, JTextArea txtLogPanel) {
		/*riceve il risultato di ReadCsvFile.checkRecords e txtFileOrdered e restituisce
		ReadCsvFile.checkRecords ordinata per numero di views coperte, con i nuovi campi
		"Views.Covered" e "Cover.Percent"
		*/
		
		//inizializzo variabili
		int numViews =  txtFileOrdered.size();
		int headerLength = table[0].length + 2;
		int numId = table.length; //numId � uguale al numero di ID differenti pi� l'intestazione 
		int [] num = new int [numId]; 
		String[][] tableOrdered = new String[numId][headerLength];
		String[] appoggio = new String [headerLength];
		String troncato = null;
		float percent = 0;	

		for (int j=0; j<numId; j++){        //inizializzo num []
		    num[j] = 0;
		}	    		
		
		for (int j=0; j<numId; j++){        //inizializzo tableOrdered
		    for (int i=0; i<headerLength; i++){
		    	tableOrdered [j][i] = "";
		    }	    	
		}		
		
		tableOrdered[0][headerLength-1] = "Cover.Percent"; //aggiungo due nuovi campi
		tableOrdered[0][headerLength-2] = "Views.Covered";
		
		for (int j=0; j<numId; j++){        //copio table[][] in tableOrdered
		    for (int i=0; i<(headerLength-2); i++){
		    	tableOrdered [j][i] = table[j][i];
		    }	    	
		}
		
		for (int j=1; j<numId; j++){        //inizializzo i due nuovi campi
		    for (int i=(headerLength-2); i<=(headerLength-1); i++){
		    	tableOrdered [j][i] = "0";
		    }	    	
		}	
		
		for(int k=0; k<numViews; k++){	//scorro ogni riga di txtFileOrdered
			
			int numString = txtFileOrdered.get(k).split(" ").length; 
			String [] eachString = new String[numString];  
			eachString = txtFileOrdered.get(k).split(" ");  //divido ogni stringa e creo array eachString[] di singole stringhe
			
			for (int t=0; t<numString; t++){
				
				String[][] request = new String [numString][2];
				request[t]= eachString[t].split(":"); //creo "matrice" request[][] contenente in 1a colonna i campi, in 2a colonna i rispettivi valori
				
				for (int n=0; n < (headerLength-2); n++){ //scorro i campi
					
					if (request[t][0].compareTo(tableOrdered[0][n])==0){  //trovo quale campo vado ad esaminare
						
						for(int m=1; m <numId; m++){
							
							if (tableOrdered[m][n].equals(request[t][1])){ //e valuto se c'� corrispondenza nel valore
								num[m]++; //se c'�corrispondeza incremento num [] nella posizione del record in esame							
								if (num[m] == numString){ //se vero significa che il record in posizione j-esima ha soddisfatto tutti i requisiti della view
									int p = Integer.parseInt(tableOrdered[m][headerLength-2]);
									p ++;	//dunque incremento "Views.Covered"
									tableOrdered[m][headerLength-2] = String.valueOf(p);		
								}
							}
							
							else if (tableOrdered[m][n].contains(request[t][1]) && n!=0){ //e valuto se c'� corrispondenza nel valore
								num[m]++; //se c'�corrispondeza incremento num [] nella posizione del record in esame							
								if (num[m] == numString){ //se vero significa che il record in posizione j-esima ha soddisfatto tutti i requisiti della view
									int p = Integer.parseInt(tableOrdered[m][headerLength-2]);
									p ++;	//dunque incremento "Views.Covered"
									tableOrdered[m][headerLength-2] = String.valueOf(p);		
								}
							}
							
						}						
					}
				}
			}
			for (int j=1; j<numId; j++){        
				num[j] = 0;
			}
		
		}
	
	for (int j=1; j<numId; j++){        //ricavo percentuale
	    
		float numberCover = Float.valueOf(tableOrdered[j][headerLength-2]);
		percent = (numberCover / numViews) * 100;
		troncato = String.format("%.2f", percent);
		tableOrdered[j][headerLength-1] = troncato + "%";
		percent = 0;
		troncato = null;
	}
	
	for (int j=2; j<numId; j++){      //ordino tableOrdered per coperture
	    
		if (Integer.parseInt(tableOrdered[j][headerLength-2]) > Integer.parseInt(tableOrdered[j-1][headerLength-2])){
			appoggio = tableOrdered[j];
			tableOrdered[j] = tableOrdered[j-1];
			tableOrdered[j-1] = appoggio;
			j=1; 
		}
			
	}
	
	for (int j=1; j<numId; j++){        //Stampo tableOrdered
    	for (int i=0; i<headerLength; i++){
    		
    		if (!tableOrdered[j][i].equals("")){
    			if (i==(headerLength-1))
    				if(txtLogPanel == null){
    					System.out.println(tableOrdered[0][i] + ": " + tableOrdered[j][i]);
    				} else {
    					txtLogPanel.append(tableOrdered[0][i] + ": " + tableOrdered[j][i] + "\n");
    				}
    			else
    				if(txtLogPanel == null){
        				System.out.print(tableOrdered[0][i] + ": " + tableOrdered[j][i] + ", ");
    				} else {
    					txtLogPanel.append(tableOrdered[0][i] + ": " + tableOrdered[j][i] + ", ");
    				}
			}
    	}
	}
	return  tableOrdered;
	}

	/**
	 * This method calculate number of the records covered by a view. It receive the ArrayList<String> txtFileOrdered and String [][] table
	 * (result of ReadCsvFile.checkRecords) and return the String[][] viewsOrdered, that is equals to txtFileOrdered, but it contains one more field
	 * "Records.Covered" (the views are contained in txtFileOrdered and the records are contained in table). The String[][] viewsOrdered is ordered
	 * by the value "Records.Covered".
	 * @param table
	 * @param txtFileOrdered
	 * @return viewsOrdered
	 */
	public static  String[][] orderViews (String [][] table, ArrayList<String> txtFileOrdered, JTextArea txtLogPanel) {		
		//inizializzo le variabili
		
		int headerLength = table[0].length;
		int numViews =  txtFileOrdered.size();
		String[][] viewsOrdered = new String[numViews][3]; //conterr� la view, la sua copertura e la sua copertura percentuale 
		int numId = table.length; //numId � uguale al numero di ID differenti pi� l'intestazione
		int [] num = new int [numId];
		String[] appoggio = new String [2];
		String[] percent = new String [numViews];
		
		
		for (int j=0; j<numViews; j++){		//inizializzo viewOrdinate
		    viewsOrdered [j][0] = txtFileOrdered.get(j);
		    viewsOrdered [j][1] = "0";
		    viewsOrdered [j][2] = "";		    
		}
		
		for (int j=0; j<numId; j++){	//inizializzo num []
		    num[j] = 0;
		}
	
		for(int k=0; k<numViews; k++){	//scorro ogni riga di txtFileOrdered
			
			int numString = txtFileOrdered.get(k).split(" ").length; 
			String [] eachString = new String[numString];  
			eachString = txtFileOrdered.get(k).split(" ");  //divido ogni stringa e creo array eachString[] di singole stringhe
			
			for (int t=0; t<numString; t++){
				
				String[][] request = new String [numString][2];
				request[t]= eachString[t].split(":"); //creo "matrice" request[][] contenente in 1a colonna i campi, in 2a colonna i rispettivi valori
				
				for (int n=0; n < (headerLength); n++){ //scorro i campi
					
					if (request[t][0].compareTo(table[0][n])==0){  //trovo quale campo vado ad esaminare
						
						for(int m=1; m <numId; m++){
							if (table[m][n].equals(request[t][1])){ //e valuto se c'� corrispondenza nel valore
								num[m]++; //se c'� corrispondeza incremento num [] nella posizione del record in esame
								if (num[m] == numString){ //se vero significa che il record in posizione j-esima ha soddisfatto tutti i requisiti della view...
									int p = Integer.parseInt(viewsOrdered[k][1]);
									p ++;	//...dunque incremento "recors.Covered"
									viewsOrdered[k][1] = String.valueOf(p);
								}
							}
							else if (table[m][n].contains(request[t][1]) && n!=0){ //e valuto se c'� corrispondenza nel valore
								num[m]++; //se c'� corrispondeza incremento num [] nella posizione del record in esame
								if (num[m] == numString){ //se vero significa che il record in posizione j-esima ha soddisfatto tutti i requisiti della view...
									int p = Integer.parseInt(viewsOrdered[k][1]);
									p ++;	//...dunque incremento "recors.Covered"
									viewsOrdered[k][1] = String.valueOf(p);
								}
							}
							
						}						
					}
				}
			}
			
			for (int j=0; j<numId; j++){	//riinizializzo num []
				num[j] = 0;
			}

		}
		
		for (int j=1; j<numViews; j++){		//ordino viewOrdinate per coperture
		    
			if (Integer.parseInt(viewsOrdered[j][1]) > Integer.parseInt(viewsOrdered[j-1][1])){
				appoggio = viewsOrdered[j];
				viewsOrdered[j] = viewsOrdered[j-1];
				viewsOrdered[j-1] = appoggio;
				j=0; 
			}
		}
		
		for (int j=0; j<numViews; j++){
           	float numberCover = Float.valueOf(viewsOrdered[j][1]);
           	float f = (numberCover / (numId-1)) * 100;
           	viewsOrdered[j][2] = String.format("%.2f", f) + "%";
        }
	
		for (int j=0; j<numViews; j++){        //Stampo viewOrdinate
			if(txtLogPanel==null){
				System.out.println("View:" + viewsOrdered[j][0] + ",Records.covered:" + viewsOrdered[j][1] + ",Cover.percent:" + viewsOrdered[j][2]);
				} else {
				txtLogPanel.append("View:" + viewsOrdered[j][0] + ",Records.covered:" + viewsOrdered[j][1] + ",Cover.percent:" + viewsOrdered[j][2] + "\n");
			}
		}
		return viewsOrdered;
	}
	
	/**
	 * * This method calculate the number of the records covered by a view and eliminate the "redundant" views. A view is redundant when it covers a group
	 * of records that is a subset of the records covered by another view. This method receive the ArrayList<String> txtFileOrdered and String [][] table
	 * (result of ReadCsvFile.checkRecords) and return the String[][] finalTable, that is equals to txtFileOrdered, but it contains one more field
	 * "Records.Covered" (the views are contained in txtFileOrdered and the records are contained in table), without the redundant views. The String[][]
	 * finalTable is ordered by the value "Records.Covered".
	 * @param txtFileOrdered
	 * @param table
	 * @param txtLogPanel 
	 * @return finalTable
	 */
	public static  String[][] removeRedundantViews (ArrayList<String> txtFileOrdered, String[][] table, JTextArea txtLogPanel) {
	//inizializzo le variabili

		int headerLength = table[0].length;
		int numId = table.length;  //numId � uguale al numero di ID differenti pi� l'intestazione
		int numViews =  txtFileOrdered.size();
        String[][] idCovered = new String[numViews][numId]; //conterr� gli id dei film coperti dall'n-esima view
        String[][] viewsWithoutRedundance = new String [numViews][2]; //conterr� le views non ridondanti e la loro rispettiva copertura
        int [] num = new int [numId];
        String[] appoggioviewsWithoutRedundance = new String [2];  
        String[] appoggioidCovered;
        float percent = 0;
        
        for (int j=0; j<numViews; j++){  //inizializzo idCovered
           	for (int i=0; i<numId; i++)
           		idCovered [j][i] = "";
        }
            
        for (int j=0; j<numViews; j++){  //inizializzo viewsWithoutRedundance
        	viewsWithoutRedundance [j][0] = txtFileOrdered.get(j);
            viewsWithoutRedundance [j][1] = "0";
        }
           
        for (int j=0; j<numId; j++){  //inizializzo num []
        	num[j] = 0;
        }
                           
        for(int k=0; k<numViews; k++){  //scorro ogni riga di txtFileOrdered
                                   
          	int numString = txtFileOrdered.get(k).split(" ").length;
            String [] eachString = new String[numString];  
            eachString = txtFileOrdered.get(k).split(" ");  //divido ogni stringa e creo array eachString[] di singole stringhe
                   
              	for (int t=0; t<numString; t++){
              		String[][] request = new String [numString][2];
                    request[t]= eachString[t].split(":"); //creo "matrice" request[][] contenente in 1a colonna i campi, in 2a colonna i rispettivi valori
                    for (int n=0; n < (headerLength); n++){ //scorro i campi
                      	if (request[t][0].compareTo(table[0][n])==0){  //trovo quale campo vado ad esaminare
                    		for(int m=1; m <numId; m++){
                      			
                    			if (table[m][n].equals(request[t][1])){ //e valuto se c'� corrispondenza nel valore
                       				num[m]++; //se c'� corrispondeza incremento num [] nella posizione del record in esame
                                    if (num[m] == numString){ //se vero significa che il record in posizione j-esima ha soddisfatto tutti i requisiti della view...
                                       	int p = Integer.parseInt(viewsWithoutRedundance[k][1]);
                                        p ++;   //...dunque incremento dunque incremento il numero di records coperti
                                        viewsWithoutRedundance[k][1] = String.valueOf(p);
                                        for (int j=0; j<numViews; j++){
                                           	if (j==k){
                                           		for (int i=0; i<numId; i++){
                                           			if (idCovered[j][i].equals("")){
                                           				idCovered [j][i] = table[m][0];
                                           				i=numId;
                                           			}    
                                           		}
                                           	}
                                        }
                                    }
                      			}
                    			
                    			else if (table[m][n].contains(request[t][1]) && n!=0){ //e valuto se c'� corrispondenza nel valore
                       				num[m]++; //se c'� corrispondeza incremento num [] nella posizione del record in esame
                                    if (num[m] == numString){ //se vero significa che il record in posizione j-esima ha soddisfatto tutti i requisiti della view...
                                       	int p = Integer.parseInt(viewsWithoutRedundance[k][1]);
                                        p ++;   //...dunque incremento dunque incremento il numero di records coperti
                                        viewsWithoutRedundance[k][1] = String.valueOf(p);
                                        for (int j=0; j<numViews; j++){
                                           	if (j==k){
                                           		for (int i=0; i<numId; i++){
                                           			if (idCovered[j][i].equals("")){
                                           				idCovered [j][i] = table[m][0];
                                           				i=numId;
                                           			}    
                                           		}
                                           	}
                                        }
                                    }
                      			}
                    		}                                              
                      	}
                    }		
              	}
                   
            for (int j=0; j<numId; j++){  //riinizializzo num []
                	num[j] = 0;
            }

        }
                   
        for (int j=1; j<numViews; j++){  //ordino viewsWithoutRedundance e idCovered rispettivi per copertura
        	if (Integer.parseInt(viewsWithoutRedundance[j][1]) > Integer.parseInt(viewsWithoutRedundance[j-1][1])){
        		appoggioviewsWithoutRedundance = viewsWithoutRedundance[j];
        		viewsWithoutRedundance[j] = viewsWithoutRedundance[j-1];
                viewsWithoutRedundance[j-1] = appoggioviewsWithoutRedundance;
                appoggioidCovered = idCovered[j];
                idCovered[j] = idCovered[j-1];
                idCovered[j-1] = appoggioidCovered;
                j=0;
            }
        }
        
        for (int k=numViews-1; k>-1; k--){  //scorro gli array creati...
        	for(int j=0; j<numViews; j++){
        		if (!viewsWithoutRedundance[k][0].equals("") && k!=j && !viewsWithoutRedundance[j][0].equals("")){
        			if (Integer.parseInt(viewsWithoutRedundance[k][1]) <= Integer.parseInt(viewsWithoutRedundance[j][1])){  //... se una view ha una copertura <= di un'altra...
        				if (idCovered[j].equals(idCovered[k])){
        					if ((viewsWithoutRedundance[k][0].split(" ").length >= viewsWithoutRedundance[j][0].split(" ").length) && k!=j){
        						viewsWithoutRedundance[k][0] = "";
                			}
                		}
                		else {
                			Collection c = new ArrayList();
                			Collection d = new ArrayList();
                			for (int i=0; i<numId; i++){
                				if (!idCovered[j][i].equals(""))
                					c.add(idCovered[j][i]);
                				if (!idCovered[k][i].equals(""))
                					d.add(idCovered[k][i]);
                			}
                    	if (c.containsAll(d))
                    		viewsWithoutRedundance[k][0] = "";
                		}
                    		
                    }
                	else j=numViews;
                }
        	}
        }
            
        for (int j=1; j<numViews; j++){  //ordino viewsWithoutRedundance accodando le stringhe vuote
        	if (viewsWithoutRedundance[j-1][0].equals("") && !viewsWithoutRedundance[j][0].equals("")){
               viewsWithoutRedundance[j-1][0] = viewsWithoutRedundance[j][0];
               viewsWithoutRedundance[j-1][1] = viewsWithoutRedundance[j][1];
               viewsWithoutRedundance[j][0] = "";
               j=1;
            }
        }
        
        int counterVoidString = 0;
           
        for (int j=0; j<numViews; j++){  //conto le stringhe vuote
        	if (viewsWithoutRedundance[j][0].equals(""))
        		counterVoidString ++;
        }
		
        numViews = numViews - counterVoidString;		
        String [][] finalTable = new String [numViews][3];
		
        for (int j=0; j<(numViews); j++){   //inizializzo finalTable
        	finalTable[j][0] = "";
            finalTable[j][1] = "";
            finalTable[j][2] = "";
        }
		
        for (int j=0; j<(numViews); j++){  //riempio finalTable con le stringhe non vuote di viewsWithoutRedundance  
          	finalTable[j][0] = viewsWithoutRedundance[j][0];
          	finalTable[j][1] = viewsWithoutRedundance[j][1];
        }
		
        for (int j=0; j<numViews; j++){
           	float numberCover = Float.valueOf(finalTable[j][1]);
           	percent = (numberCover / (numId-1)) * 100;
           	finalTable [j][2] = String.format("%.2f", percent);
           	percent = 0;			
        }
		
        for (int j=0; j<numViews; j++){
          	finalTable [j][2] = finalTable [j][2] + "%";
        }
		
        for (int j=0; j<numViews; j++){  //stampo finalTable
        	if(txtLogPanel==null){
        		System.out.println( "View:" + finalTable[j][0] + ",Record.Covered:" + finalTable[j][1] + ",Cover.Percent:" + finalTable [j][2]);
        	}
        	else {
            	txtLogPanel.append( "View:" + finalTable[j][0] + ",Record.Covered:" + finalTable[j][1] + ",Cover.Percent:" + finalTable [j][2] + "\n");
            }
        }		
		
        return finalTable;
	}
}